/**
 * 
 */
package adnotatio.rdf;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import adnotatio.common.xml.SerializationUtil;
import adnotatio.common.xml.XMLElement;
import adnotatio.common.xml.XMLName;
import adnotatio.common.xml.XMLNode;
import adnotatio.common.xml.XMLText;

class AtomParser {

    private class ContentProvider extends Provider {

        protected Object getValue(XMLElement e) {
            StringBuffer buf = new StringBuffer();
            SerializationUtil.serializeNodeContent(e, buf);
            return buf.toString();
        }

    }

    private class DateProvider extends ContentProvider {
        protected Object getValue(XMLElement e) {
            StringBuffer buf = new StringBuffer();
            SerializationUtil.serializeNodeContent(e, buf);
            String value = buf.toString();
            Date result = DateParser.parse(value);
            return result;
        }
    }

    // static class NamespaceManager {
    //
    // Map fNamespaceToPrefix = new HashMap();
    //
    // Map fPrefixToNamespace = new HashMap();
    //
    // public void add(String prefix, String namespace) {
    // fPrefixToNamespace.put(prefix, namespace);
    // fNamespaceToPrefix.put(namespace, prefix);
    // }
    //
    // public String getNamespace(String prefix) {
    // return (String) fPrefixToNamespace.get(prefix);
    // }
    //
    // public String getPrefix(String namespace) {
    // return (String) fNamespaceToPrefix.get(namespace);
    // }
    //
    // public String getUrl(String name) {
    // int pos = name.indexOf(':');
    // if (pos > 0) {
    // String prefix = name.substring(0, pos);
    // name = name.substring(pos + 1);
    // String namespace = getNamespace(prefix);
    // if (namespace != null) {
    // name = namespace + name;
    // }
    // }
    // return name;
    // }
    // }
    //
    private static abstract class Provider {

        protected abstract Object getValue(XMLElement e);

    }

    private class RDFLinkProvider extends RecursiveProvider {

        private ID getAttribute(String ns, String label, XMLElement e) {
            XMLName n = new XMLName(ns, null, label);
            String str = e.getAttributeValue(n);

            n = new XMLName(str);
            ID id = new ID(n.getNamespace(), n.getLocalName());
            return id;
        }

        public Object getValue(XMLElement e) {
            ID url = getAttribute(NS_RDF, "resource", e);
            if (url != null) {
                Resource r = fModel.getResource(url, true);
                return r;
            } else {
                return super.getValue(e);
            }
        }

    }

    private class RecursiveProvider extends Provider {

        protected void appendChildren(Resource resource, XMLElement e) {
            int len = e.getChildNumber();
            XMLNode child;
            for (int i = 0; i < len; i++) {
                child = e.getChild(i);
                if (!(child instanceof XMLElement))
                    continue;
                XMLElement ch = (XMLElement) child;
                ID propertyID = getID(ch);
                Object value = getElementValue(ch);
                if (value != null) {
                    PropertyValue oldValue = resource.getValue(propertyID);
                    if (oldValue != null) {
                        Container collection = oldValue.getAsContainer();
                        collection.addValue(value);
                    } else {
                        resource.setValue(propertyID, value);
                    }
                }
            }
        }

        /**
         * @param e
         * @return
         */
        private Object getElementValue(XMLElement e) {
            Provider provider = getProvider(e);
            Object value;
            if (provider != null) {
                value = provider.getValue(e);
            } else {
                StringBuffer buf = new StringBuffer();
                int len = e.getChildNumber();
                boolean hasElement = false;
                for (int i = 0; !hasElement && i < len; i++) {
                    XMLNode child = e.getChild(i);
                    hasElement = child instanceof XMLElement;
                    if (!hasElement && (child instanceof XMLText)) {
                        XMLText d = (XMLText) child;
                        buf.append(d.getText());
                    }
                }
                if (hasElement) {
                    Resource r = newResource(e);
                    appendChildren(r, e);
                    value = r;
                } else {
                    value = buf.length() > 0 ? buf.toString() : null;
                }
            }
            return value;
        }

        protected Object getValue(XMLElement e) {
            Resource r = newResource(e);
            appendChildren(r, e);
            return r;
        }

    }

    private class URLProvider extends RecursiveProvider {

        private void addReference(
            Resource r,
            XMLElement e,
            String ns,
            String name) {
            String value = e.getAttributeValue(new XMLName(ns, null, name));
            if (value != null) {
                ID propertyID = new ID(ns, name);
                ID objectID = new ID(value);
                r.setValue(propertyID, objectID);
            }
        }

        public Object getValue(XMLElement e) {
            Resource r = newResource(e);
            addReference(r, e, NS_ATOM, "href");
            addReference(r, e, NS_ATOM, "rel");
            addReference(r, e, NS_ATOM, "type");
            addReference(r, e, NS_APP, "edit");
            addReference(r, e, NS_ATOM, "src");
            appendChildren(r, e);
            return r;
        }
    }

    public static final String NS_APP = "http://www.w3.org/2007/app";

    public static final String NS_ATOM = "http://www.w3.org/2005/Atom";

    public static final String NS_RDF = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";

    Map fMap = new HashMap();

    Model fModel = new Model(null, new ID());

    public AtomParser() {
        initProviderMap();
    }

    protected void addProvider(String ns, String name, Provider provider) {
        String url = ns + name;
        fMap.put(url, provider);
    }

    protected Provider getDefaultProvider() {
        Provider provider = (Provider) fMap.get("");
        return provider;
    }

    protected ID getID(XMLElement e) {
        XMLName name = e.getNodeName();
        return new ID(name.getNamespace(), name.getLocalName());
    }

    protected Provider getProvider(XMLElement e) {
        ID url = getID(e);
        Provider provider = (Provider) fMap.get(url);
        return provider;
    }

    private void initProviderMap() {
        ContentProvider contentProvider = new ContentProvider();
        addProvider(NS_ATOM, "title", contentProvider);

        Provider dateProvider = new DateProvider();
        addProvider(NS_ATOM, "updated", dateProvider);

        URLProvider urlProvider = new URLProvider();
        addProvider(NS_APP, "collection", urlProvider);
        addProvider(NS_ATOM, "link", urlProvider);

        RDFLinkProvider rdfLinkProvider = new RDFLinkProvider();
        addProvider(NS_RDF, "li", rdfLinkProvider);
    }

    /**
     * @param e
     * @return
     */
    private Resource newResource(XMLElement e) {
        ID id = getID(e);
        Resource r = fModel.getResource(id, true);
        return r;
    }

    public Model parse(XMLElement e) {
        Provider provider = new RecursiveProvider();
        provider.getValue(e);
        return fModel;
    }

}